package com.i360day.invoker;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.i360day.invoker.codes.decoder.Decoder;
import com.i360day.invoker.codes.decoder.JavaObjectDecoder;
import com.i360day.invoker.codes.decoder.SpringDecode;
import com.i360day.invoker.codes.encoder.Encoder;
import com.i360day.invoker.codes.encoder.JavaObjectEncoder;
import com.i360day.invoker.codes.encoder.SpringEncoder;
import com.i360day.invoker.context.HttpInvokerContext;
import com.i360day.invoker.executor.HttpComponentsHttpInvokerRequestExecutor;
import com.i360day.invoker.executor.HttpInvokerRequestExecutor;
import com.i360day.invoker.http.InvokerClient;
import com.i360day.invoker.http.httpclient.InvokerHttpClient;
import com.i360day.invoker.http.okhttp.InvokerOkHttpClient;
import com.i360day.invoker.interceptor.HttpInvokerRequestInterceptor;
import com.i360day.invoker.properties.HttpInvokerProperties;
import com.i360day.invoker.proxy.DefaultTargeterHandler;
import com.i360day.invoker.proxy.Targeter;
import com.i360day.invoker.request.HttpInvokerRequest;
import com.i360day.invoker.security.HttpInvokerSecurityAdapter;
import com.i360day.invoker.support.DefaultRemoteInvocationFactory;
import com.i360day.invoker.support.ObjectMapperRemoteInvocationFactory;
import com.i360day.invoker.support.RemoteInvocationFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author liju.z
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({HttpInvokerProperties.class})
public class InvokerBasicConfiguration {
    /**
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public HttpInvokerContext httpInvokerContext() {
        return new DefaultInvokerContext();
    }

    /**
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public HttpInvokerSecurityAdapter httpInvokerSecurityAdapter() {
        return new NullHttpInvokerSecurityAdapter();
    }

    /**
     * 创建请求执行器
     * @param httpInvokerRequestInterceptor
     * @param invokerRequest
     * @param decoder
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public HttpInvokerRequestExecutor httpInvokerRequestExecutor(HttpInvokerRequestInterceptor httpInvokerRequestInterceptor,
                                                                 HttpInvokerRequest invokerRequest,
                                                                 Decoder decoder) {
        return new HttpComponentsHttpInvokerRequestExecutor(invokerRequest, httpInvokerRequestInterceptor, decoder);
    }

    /**
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(value = "spring.invoker.hystrix.enabled", havingValue = "false")
    public Targeter defaultTargeter() {
        return new DefaultTargeterHandler();
    }
    /**
     * http
     *
     * @author liju.z
     */
    @Configuration(proxyBeanMethods = false)
    public static class HttpInvokerRequestClient {
        /**
         * 创建httpClient request
         *
         * @return
         */
        @ConditionalOnMissingBean
        @Bean(destroyMethod = "close")
        @ConditionalOnProperty(value = "spring.invoker.request.client", havingValue = "httpClient", matchIfMissing = true)
        public InvokerClient defaultHttpClient(HttpInvokerProperties properties) {
            return InvokerHttpClient.create(properties);
        }

        /**
         * 创建okHttp request
         *
         * @return
         */
        @ConditionalOnMissingBean
        @Bean(destroyMethod = "close")
        @ConditionalOnProperty(value = "spring.invoker.request.client", havingValue = "okhttp")
        public InvokerClient okHttpClient(HttpInvokerProperties properties) {
            return InvokerOkHttpClient.create(properties);
        }
    }

    /**
     * json Serializable
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnProperty(value = "spring.invoker.serializable", havingValue = "json", matchIfMissing = true)
    public static class JsonSerializableConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public Decoder springDecode(ObjectFactory<HttpMessageConverters> messageConverters, HttpInvokerProperties httpInvokerProperties) {
            return new SpringDecode(messageConverters, httpInvokerProperties);
        }

        @Bean
        @ConditionalOnMissingBean
        public RemoteInvocationFactory objectMapperRemoteInvocationFactory(ObjectMapper objectMapper) {
            return new ObjectMapperRemoteInvocationFactory(objectMapper);
        }

        @Bean
        @ConditionalOnMissingBean
        public Encoder springEncoder(ObjectFactory<HttpMessageConverters> messageConverters, HttpInvokerProperties httpInvokerProperties) {
            return new SpringEncoder(messageConverters, httpInvokerProperties.getResponse());
        }
    }

    /**
     * jdk Serializable
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnProperty(value = "spring.invoker.serializable", havingValue = "jdk")
    public static class JavaSerializableConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public Decoder javaObjectDecoder() {
            return new JavaObjectDecoder();
        }

        @Bean
        @ConditionalOnMissingBean
        public Encoder javaObjectEncoder() {
            return new JavaObjectEncoder();
        }

        @Bean
        @ConditionalOnMissingBean
        public RemoteInvocationFactory remoteInvocationFactory() {
            return new DefaultRemoteInvocationFactory();
        }
    }
}
